<?php
namespace PsiFramework\Classes\Util\Database\QueryInterpreter;

import("Classes.Util.Database.QueryInterpreter.SqlQueryInterpreter");

use \PsiFramework\Classes\Model\ModelInterface\IPrimitiveObject;
use \PsiFramework\Classes\Util\Database\Query\SqlAddColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlChangeColumnType;
use \PsiFramework\Classes\Util\Database\Query\SqlLimit;
use \psiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlType;
use \PsiFramework\Classes\Util\Database\QueryInterpreter\SqlQueryInterpreter;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The PostgresSqlQueryInterpreter is used to translate SqlQueries
 * to string queries for PgSql
 *
 * @package Util.QueryInterpreter
 */
class PostgresSqlQueryInterpreter extends SqlQueryInterpreter
{

    protected function getVarDelimiter()
    {
        return '"' ;
    }

    protected function getSqlValue(/*mixed*/ $value)
    {
        if (!is_null($value)) {
            if ($value instanceof IPrimitiveObject) {
                $value = $value->serialize();
            } else if (is_bool($value)) {
                $value = ($value)?"TRUE":"FALSE" ;
            }
            $value = "'".pg_escape_string($value)."'" ;
        } else {
            $value = 'NULL' ;
        }
        return $value ;
    }

    protected function getLimitClause(SqlLimit $limit=null)
    {
        if (isNullOrEmptyString($limit)) {
            return '' ;
        }
        if (isNullOrEmptyString($limit->getLength())) {
            return '' ;
        }

        $limitString = 'LIMIT ' . $limit->getLength();
        if (!isNullOrEmptyString($limit->getSkip())) {
            $limitString .= ' OFFSET ' . $limit->getSkip();
        }

        return $limitString ;
    }

    protected function getChangeColumn(SqlChangeColumnType $parameter=null)
    {
        $stringQuery  = 'ALTER COLUMN ' .
                        $this->getColumn(
                            $parameter->getColumn(), false, false
                        ).
                        ' TYPE '.
                        $this->getColumnType(
                            $parameter->getColumn()->getType(), false
                        ).
                        ', ALTER COLUMN '.
                        $this->getColumn(
                            $parameter->getColumn(), false, false
                        );
        
        if (!$parameter->getColumn()->getType()->canBeNull()) {
            $stringQuery .= ' SET' ;
        } else {
            $stringQuery .= ' DROP' ;
        }
        
        $stringQuery .= ' NOT NULL' ;

        return $stringQuery ;
    }

    protected function getAddColumn(SqlAddColumn $parameter=null)
    {
        return
            'ADD COLUMN ' .
            $this->getColumnDescription($parameter->getColumn());
    }

    public function getColumnType(
        SqlType  $sqlType=null,
        /*bool*/ $includeNullDeclaration=true,
        /*bool*/ $includeLength=true
    )
    {
        $type = null ;
        switch($sqlType->getType()) {
            case SqlType::$serial :
                $type = "SERIAL" ;
                break;
            case SqlType::$integer :
                $type = "INTEGER" ;
                break;
            case SqlType::$float :
                $type = "DOUBLE PRECISION" ;
                break;
            case SqlType::$double :
                $type = "DOUBLE PRECISION" ;
                break;
            case SqlType::$long :
                $type = "BIGINT" ;
                break;
            case SqlType::$varchar :
                $type = "VARCHAR" ;
                break;
            case SqlType::$char :
                $type = "CHAR" ;
                break;
            case SqlType::$text :
                $type = "TEXT" ;
                break;
            case SqlType::$dateTime :
                if ($includeLength) {
                    $type = "DATE" ;
                } else {
                    $type = "DATETIME" ;
                }
                break;
            case SqlType::$boolean :
                $type = "BOOLEAN" ;
                break;
            case SqlType::$blob :
                $type = "BLOB" ;
                break;
        }

        if (!isNullOrEmptyString($type)) {
            if (!is_null($sqlType->getLength()) && $includeLength) {
                $type .= '('.$sqlType->getLength().')' ;
            }
            if (($sqlType->getType() == SqlType::$serial
                || !$sqlType->canBeNull())
                && $includeNullDeclaration
            ) {
                $type .= ' NOT' ;
            }

            if ($includeNullDeclaration){
                $type .= ' NULL' ;
            }
        }

        return $type ;
    }

    protected function getTableDefinitionSuffix()
    {
        return 'WITH (OIDS=FALSE)' ;
    }

    public static function tableExistsQuery(
        SqlTable $table,
        /*bool*/ $view=false
    )
    {
        $t = 'tables' ;
        if ($view) {
            $t = 'views' ;
        }

        return
            'SELECT COUNT(*) AS "table_exists" '.
            'FROM "information_schema"."'.$t.'" '.
            'WHERE "table_catalog" = CURRENT_CATALOG '.
            'AND "table_schema" = \''.$table->getSchema().'\' '.
            'AND "table_name" = \''.$table->getName().'\'' ;
    }

    public static function tableStructureQuery(SqlTable $table)
    {
        return
            'SELECT '.
                '"column_name" AS "name", '.
                '"is_nullable" AS "nullable", '.
                '"data_type" AS "type", '.
                '"character_maximum_length" AS "length", '.
                '"column_default" AS "default", '.
                '\'\' AS "extra" '.
            'FROM "information_schema"."columns" '.
            'WHERE "table_catalog" = CURRENT_CATALOG '.
            'AND "table_schema" = \''.$table->getSchema().'\' '.
            'AND "table_name" = \''.$table->getName().'\'' ;
    }

}
